/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0.
 *
 * @Date: 2021-04-29 10:40:47
 * @LastEditTime: 2021-08-16 15:31:51
 * @Description:  Description of file
 * @Modify History:
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */

#include "ft_can.h"
#include "ft_can_hw.h"
#include "ft_assert.h"
#include "ft_types.h"

ft_error_t FCanSetHandler(FCan *instance_p, u32 handler_type, void *irq_callback_func, void *irq_callback_ref)
{
    ft_error_t status = FCAN_SUCCESS;
    FT_ASSERTNONVOID(instance_p != NULL);
    FT_ASSERTNONVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    switch (handler_type)
    {
    case FCAN_HANDLER_SEND:
        instance_p->send_handler = irq_callback_func;
        instance_p->send_ref = irq_callback_ref;
        break;
    case FCAN_HANDLER_RECV:
        instance_p->recv_handler = irq_callback_func;
        instance_p->recv_ref = irq_callback_ref;
        break;
    case FCAN_HANDLER_ERROR:
        instance_p->error_handler = irq_callback_func;
        instance_p->error_ref = irq_callback_ref;
        break;
    default:
        status = FCAN_FAILURE;
    }

    return status;
}

/**
 * @name: FCanTxInterrupt
 * @msg: Tx Done Isr
 * @param {FCan} *instance_p
 */
static void FCanTxInterrupt(FCan *instance_p)
{
    FCanConfig *config_p = &instance_p->config;
    FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_TEIC_MASK | FCAN_INTR_REIC_MASK);

    FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK);
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);

    if (instance_p->send_handler)
    {
        instance_p->send_handler(instance_p->send_ref);
    }
}

static void FCanErrorInterrupt(FCan *instance_p, u32 isr)
{
    u32 txerr = 0, rxerr = 0;
    FCanConfig *config_p = &instance_p->config;

    rxerr = FCAN_READREG(config_p->base_address, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_RFN_MASK;
    txerr = (FCAN_READREG(config_p->base_address, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_TFN_MASK) >> FCAN_ERR_CNT_TFN_SHIFT;

    if (instance_p->error_handler)
    {
        instance_p->error_handler(instance_p->error_ref, isr, txerr, rxerr);
    }
}

static void FCanRxInterrupt(FCan *instance_p)
{
    if (instance_p->recv_handler)
    {
        instance_p->recv_handler(instance_p->recv_ref);
    }
}

void FCanIntrHandler(void *instance_ptr)
{
    u32 irq_status;
    FCan *instance_p = (FCan *)instance_ptr;
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;
    irq_status = FCAN_READREG(config_p->base_address, FCAN_INTR_OFFSET);

    if (0 == irq_status)
    {
        return;
    }

    /* Check for the type of error interrupt and Processing it */
    if (irq_status & FCAN_INTR_TEIS_MASK)
    {
        irq_status &= ~FCAN_INTR_REIS_MASK;
        FCanTxInterrupt(instance_p);
    }

    if (irq_status & (FCAN_INTR_EIS_MASK | FCAN_INTR_RFIS_MASK |
                      FCAN_INTR_BOIS_MASK | FCAN_INTR_PEIS_MASK | FCAN_INTR_PWIS_MASK))
    {
        FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, (FCAN_INTR_EIC_MASK | FCAN_INTR_RFIC_MASK | FCAN_INTR_BOIC_MASK | FCAN_INTR_PEIC_MASK | FCAN_INTR_PWIC_MASK));
        FCanErrorInterrupt(instance_p, irq_status);
    }

    if (irq_status & FCAN_INTR_REIS_MASK)
    {
        FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK);
        FCanRxInterrupt(instance_p);
        FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIC_MASK);
        FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK);
    }
}
